package opn.srn.command.load.parser.fixed;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import opn.srn.command.load.parser.FieldMetaData;
import opn.srn.command.load.parser.GroupMetaData;
import opn.srn.command.load.parser.IParser;
import opn.srn.command.load.parser.RecordMetaData;
import opn.srn.command.load.parser.stream.IStream;
import opn.srn.command.load.parser.stream.StringStream;
import opn.srn.command.load.parser.util.ParameterUtils;
import opn.srn.command.load.parser.util.ParserUtil;
import opn.srn.common.IMessage;

public class FixedLengthParser implements IParser {

	List<FieldMetaData> fieldMetaDatas;

	@Override
	public void initialize(List<FieldMetaData> fieldMetaDatas) {
		this.fieldMetaDatas = fieldMetaDatas;
	}

	@Override
	public void parse(IStream stream, IMessage message) throws Exception {
		for (FieldMetaData fieldMetaData : fieldMetaDatas) {
			if (fieldMetaData instanceof GroupMetaData) {
				GroupMetaData groupMetaData = (GroupMetaData) fieldMetaData;
				IParser parser = groupMetaData.getParser();
				if (parser == null) {
					continue;
				}

				IStream groupStream = stream;
				if (groupMetaData.isGlobalParseScope() == false && groupMetaData.getAdditionalMetaInfo() != null) {
					FixedFieldMetaData fixedFieldMetaData = (FixedFieldMetaData) groupMetaData.getAdditionalMetaInfo();
					String value = stream.readNChar(fixedFieldMetaData.getSize(), fixedFieldMetaData.isMandatory());
					groupStream = new StringStream(value);
				}
				parser.parse(groupStream, message);
			} else if (fieldMetaData instanceof FixedFieldMetaData) {
				FixedFieldMetaData fixedFieldMetaData = (FixedFieldMetaData) fieldMetaData;

				String value = stream.readNChar(fixedFieldMetaData.getSize(), fixedFieldMetaData.isMandatory());
				message.setData(fixedFieldMetaData.getName(), value);
			}

		}
	}

	@Override
	public void load(RecordMetaData parentMetaData) throws Exception {
		List<RecordMetaData> members = parentMetaData.getMembers();
		fieldMetaDatas = new ArrayList<FieldMetaData>();
		if (members != null) {
			for (RecordMetaData metaData : members) {
				fieldMetaDatas.add(createFieldMetaData(metaData));
			}
		}
	}

	private FieldMetaData createFieldMetaData(RecordMetaData recordMetaData) throws Exception {
		Map<String, String> parameters = recordMetaData.getParameters();

		FieldMetaData fieldMetaData = null;
		List<RecordMetaData> members = recordMetaData.getMembers();
		if (members != null && members.size() > 0) {
			IParser parser = ParserUtil.createParser(recordMetaData);
			GroupMetaData groupMetaData = new GroupMetaData(recordMetaData.getName());
			groupMetaData.setParser(parser);

			boolean globalScope = (ParameterUtils.get(parameters, "scope", "global").equals("global")) ? true : false;
			if (globalScope == false) {
				groupMetaData.setAdditionalMetaInfo(createFixedFieldMetaData(recordMetaData, "local."));
			}
			groupMetaData.setGlobalParseScope(globalScope);

			fieldMetaData = groupMetaData;
		} else {
			fieldMetaData = createFixedFieldMetaData(recordMetaData, "");
		}
		return fieldMetaData;
	}

	private FixedFieldMetaData createFixedFieldMetaData(RecordMetaData recordMetaData, String prefix) throws Exception {
		Map<String, String> parameters = recordMetaData.getParameters();

		String sizeValue = parameters.get(prefix + "size");
		if (sizeValue == null) {
			throw new Exception("size attribute missing in field " + recordMetaData.getName());
		}

		int size = 0;
		try {
			size = Integer.valueOf(sizeValue);
		} catch (NumberFormatException ex) {
			throw new Exception("size attribute value is not valid number(" + sizeValue + ") in field "
					+ recordMetaData.getName());
		}

		FixedFieldMetaData fieldMetaData = new FixedFieldMetaData(recordMetaData.getName(), size);
		String mandatoryValue = parameters.get(prefix + "mandatory");
		if (mandatoryValue == null || mandatoryValue.equals("false")) {
			fieldMetaData.setMandatory(false);
		} else {
			fieldMetaData.setMandatory(true);
		}

		return fieldMetaData;
	}

	@Override
	public List<FieldMetaData> getFieldMetaDatas() {
		return fieldMetaDatas;
	}
}
